#include "test_sm.h"
#include <sea_statemachine>

// -------------------------------------------------------------------------------------------------

using namespace sea;

void test_state_machine1( );

// -------------------------------------------------------------------------------------------------

class MyStateMachine : public StateMachine
{
public:

    MyStateMachine( ): StateMachine( STATEMACHINE_NAME ) { }

    EVENT_INT( MyStateMachine, Ev_Int_MouseInside );
    EVENT_INT( MyStateMachine, Ev_Int_MouseOutside );

    ACT( MyStateMachine, doAction1 );
    ACT( MyStateMachine, doAction2 );
};

// -------------------------------------------------------------------------------------------------

ACT_IMPL( MyStateMachine, doAction1 ) { SEA_UNUSED_PARAM( ); }
ACT_IMPL( MyStateMachine, doAction2 ) { SEA_UNUSED_PARAM( ); }

// -------------------------------------------------------------------------------------------------

SM_DECLARE(
    mine,
    MyStateMachine  async_batch
    {
        Exceptions
        {
            Ev_Sys_Terminate        exit            nojump
        }
        Outside 1 20
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction2       nojump
            Ev_Sys_LastHeartbeat    doAction2       Inside

            Ev_Int_MouseInside      doAction1       nojump
        }
        Inside 100 2
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction2       nojump
            Ev_Sys_LastHeartbeat    doAction2       Dummy

            Ev_Int_MouseOutside     null            nojump
            Ev_Int_MouseOutside     doAction2       Outside

        }
        Dummy 10 2
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction1       nojump
            Ev_Sys_LastHeartbeat    exit            nojump

            Ev_Int_MouseInside      doAction1       Inside
            Ev_Int_MouseOutside     doAction2       Outside
        }
    } );


// -------------------------------------------------------------------------------------------------

// Beahvious can be inherited
class Child: public MyStateMachine
{
public:
    void doAction1( void* ACT_EVENT_PARAM( ) )
    {
        SEA_UNUSED_PARAM( );
        MyStateMachine::doAction1( ACT_EVENT_PARAM( ) );
    }

    void doAction2( void* ACT_EVENT_PARAM( ) )
    {
        SEA_UNUSED_PARAM( );
        MyStateMachine::doAction2( ACT_EVENT_PARAM( ) );
    }
};

// -------------------------------------------------------------------------------------------------

void test_state_machine( )
{
    /*{
        Child* sm;
        sm = NEW Child;
        sm->create( 0, mine );
        sm->start( StateMachine::RunParser );
        sm->release_ref( );
    }*/

    // Recursive test
    {
        test_state_machine1( );
    }
#if defined( SEA_OS_WIN32 )
    int n = 100;
#else
    int n = 30;
#endif
    MyStateMachine* sm;
    vector< StateMachine* > container;

    // PARSER mode
    {
        SEA_TRACE( " %d state machines, PARSER mode\n", n );
        container.clear( );
        for( int i = 0; i < n; i++ )
        {
            sm = NEW MyStateMachine;
            sm->create( 0, mine );
            container.insert( container.end( ), sm );
        }

        sm->start( container, StateMachine::RunParser );

        for( int i = 0; i < n; i++ ) container[ i ]->release_ref( );
    }

    Engine::terminate( );

    // SYNC mode
    {
        SEA_TRACE( " %d state machines, SYNC mode\n", n );
        container.clear( );
        for( int i = 0; i < n; i++ )
        {
            sm = NEW MyStateMachine;
            sm->create( 0, mine );
            container.insert( container.end( ), sm );
        }

        sm->start( container, StateMachine::RunSync );

        for( int i = 0; i < n; i++ ) container[ i ]->release_ref( );
    }

    Engine::terminate( );

    // ASYNC_BATCH
    {
        SEA_TRACE( " %d state machines, ASYNC_BATCH mode\n", n );
        container.clear( );
        for( int i = 0; i < n; i++ )
        {
            sm = NEW MyStateMachine;
            sm->create( 0, mine );

            container.insert( container.end( ), sm );
        }

        sm->start( container, StateMachine::RunAsyncBatch );
        sm->wait_manager_to_finish( );

        for( int i = 0; i < n; i++ ) container[ i ]->release_ref( );
    }

    Engine::terminate( );

    // ASYNC mode
#if 1
    {
        SEA_TRACE( " %d state machines, ASYNC mode\n", n );

        container.clear( );
        int n = 100;
        for( int i = 0; i < n; i++ )
        {
            sm = NEW MyStateMachine;
            sm->create( 0, mine );

            container.insert( container.end( ), sm );
        }

        sm->start( container, StateMachine::RunAsync );

        for( int i = 0; i < n; i++ )
        {
            container[ i ]->wait_manager_to_finish( );
            container[ i ]->release_ref( );
        }
    }
#endif
    Engine::terminate( );

}

// -------------------------------------------------------------------------------------------------

SM_DECLARE(
    rec,
    Rec  async
    {
        Exceptions
        {
            Ev_Sys_Terminate        exit        nojump
        }
        Idle
        {
            Ev_Sys_OnEntry          doRec       nojump
            Ev_Sys_OnEntry          exit        nojump
        }
    } 
);

// -------------------------------------------------------------------------------------------------

int nesting = 5;

class Rec : public StateMachine
{
public:
    Rec( ): StateMachine( STATEMACHINE_NAME ) { }
    ACT( Rec, doRec );
};

// -------------------------------------------------------------------------------------------------

ACT_IMPL( Rec, doRec ) 
{
    SEA_UNUSED_PARAM( );

    if( nesting )
    {
        StateMachine* sm = NEW Rec;
        sm->create( 0, rec );
        sm->start( this, StateMachine::RunWithParent );
        nesting--;
        sm->release_ref( );
    }
}

// -------------------------------------------------------------------------------------------------

void test_state_machine1( )
{
    StateMachine* sm = NEW Rec;
    sm->create( 0, rec );
    sm->start( StateMachine::RunAsync );
    sm->wait_manager_to_finish( );
    sm->release_ref( );
    Engine::terminate( );
}


// -------------------------------------------------------------------------------------------------

/*

1->1 -> wait
1->N -> wait all

*/

// -------------------------------------------------------------------------------------------------
